package com.example.lock.zk.impl;

import com.example.util.ZkUtils;
import org.I0Itec.zkclient.IZkDataListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.CountDownLatch;

/**
 * @Description: ZK 实现分布式锁实现
 * @Author: mingtian
 * @CreateDate: 2020/12/5 15:27
 * @Version: 1.0
 */
@Component
public class ZkLockImpl extends AbstractLockTemplate {

    @Value("${server.port}")
    private String serverPort;

    /**
     * 打印日志
     */
    private Logger logger = LoggerFactory.getLogger(ZkLockImpl.class);

    /**
     * 分布式锁路径
     */
    private String lockPath = "/zk_lock";

    /**
     * 多个jvm同时在zk上创建一个临时节点/lockPath,
     * 最终只能够有一个jvm创建临时节点成功，如果能够创建
     * 临时节点成功jvm 表示获取锁成功能够正常执行业务逻辑，
     * 如果没有创建临时节点成功的jvm，则表示获取锁失败。
     * 获取锁失败之后，可以采用不断重试策略，重试多次
     * 获取锁失败之后，当前的jvm就进入到阻塞状态。
     */

    /**
     * 线程阻塞使用
     */
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    @Override
    protected boolean tryLock() {
        try {
            ZkUtils.createZkClient().createEphemeral(lockPath);
            return true;
        } catch (Exception e) {
            return false;
        }

    }

    @Override
    protected void waitLock() {
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                logger.info("serverPort:{}该节点已经释放锁", serverPort);
                // 当节点被删除之后，唤醒阻塞的线程
                countDownLatch.countDown();
            }
        };
        // 事件监听
        ZkUtils.getZkClient().subscribeDataChanges(lockPath, iZkDataListener);
        try {
            // 如果已经存在节点的话 则进行阻塞
            if (ZkUtils.getZkClient().exists(lockPath)) {
                // 线程阻塞
                countDownLatch.await();
            }
        } catch (Exception e) {
        }
        // 当我们节点删除之后,我们应该重新被唤醒  移除监听事件
        ZkUtils.getZkClient().unsubscribeDataChanges(lockPath, iZkDataListener);
    }


}